//---------------------------------------------------------------------
// File: FileValidateStep.cs
// 
// Summary: 
//
//---------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------

namespace Microsoft.Services.BizTalkApplicationFramework.BizUnit
{
	using System;
	using System.IO;
	using System.Xml;
	using System.Threading;

	/// <summary>
	/// The FileValidateStep reads a FILE from a given locaton and validates the contents.
	/// </summary>
	/// 
	/// <remarks>
	/// The following shows an example of the Xml representation of this test step.
	/// 
	/// <code escaped="true">
	///	<TestStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.FileValidateStep">
	///		<Timeout>2000</Timeout>
	///		<Directory>C:\Recv2\</Directory>
	///		<SearchPattern>TransactionId_*.xml</SearchPattern>
	///		<DeleteFile>true</DeleteFile>
	///			
	///		<!-- Note: ContextLoader Step could be any generic validation step -->	
	///		<ContextLoaderStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.RegExContextLoader">
	///			<RegEx contextKey="HTTP_Url">/def:html/def:body/def:p[2]/def:form</RegEx>
	///			<RegEx contextKey="ActionID">/def:html/def:body/def:p[2]/def:form/def:input[3]</RegEx>
	///			<RegEx contextKey="ActionType">/def:html/def:body/def:p[2]/def:form/def:input[4]</RegEx>
	///			<RegEx contextKey="HoldEvent">/def:html/def:body/def:p[2]/def:form/def:input[2]</RegEx>
	///		</ContextLoaderStep>
	///	
	///		<!-- Note: Validation step could be any generic validation step -->	
	///		<ValidationStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.XmlValidationStep">
	///			<XmlSchemaPath>.\TestData\PurchaseOrder.xsd</XmlSchemaPath>
	///			<XmlSchemaNameSpace>http://SendMail.PurchaseOrder</XmlSchemaNameSpace>
	///			<XPathList>
	///				<XPathValidation query="/*[local-name()='PurchaseOrder' and namespace-uri()='http://SendMail.PurchaseOrder']/*[local-name()='PONumber' and namespace-uri()='']">PONumber_0</XPathValidation>
	///			</XPathList>
	///		</ValidationStep>			
	///	</TestStep>
	///	</code>
	///	
	///	<list type="table">
	///		<listheader>
	///			<term>Tag</term>
	///			<description>Description</description>
	///		</listheader>
	///		<item>
	///			<term>Timeout</term>
	///			<description>Timeout to wait for the FILE to be written, in milisecs</description>
	///		</item>
	///		<item>
	///			<term>Directory</term>
	///			<description>The directory where the FILE is located</description>
	///		</item>
	///		<item>
	///			<term>SearchPattern</term>
	///			<description>The search pattern, such as "*.txt"</description>
	///		</item>
	///		<item>
	///			<term>DeleteFile</term>
	///			<description>true if the file should be deleted, false if it should not</description>
	///		</item>
	///		<item>
	///			<term>ContextLoaderStep</term>
	///			<description>The configuration for the context loader step used to load data into the BizUnit context which may be used by subsequent test steps<para>(optional)</para></description>
	///		</item>
	///		<item>
	///			<term>ValidationStep</term>
	///			<description>The configuration for the validation step used to validate the contents of the file, the validation step should implement IValidationTestStep<para>(optional)</para></description>
	///		</item>
	///	</list>
	///	</remarks>
	public class FileValidateStep : ITestStep
	{
		/// <summary>
		/// ITestStep.Execute() implementation
		/// </summary>
		/// <param name='testConfig'>The Xml fragment containing the configuration for this test step</param>
		/// <param name='context'>The context for the test, this holds state that is passed beteen tests</param>
		public void Execute(XmlNode testConfig, Context context)
		{
			MemoryStream data = null;

			try
			{
				string directory = context.ReadConfigAsString( testConfig, "Directory" );
				string searchPattern = context.ReadConfigAsString( testConfig, "SearchPattern" );
				bool deleteFile = context.ReadConfigAsBool( testConfig, "DeleteFile" );
				double timeout = context.ReadConfigAsDouble( testConfig, "Timeout" );
				XmlNode validationConfig = testConfig.SelectSingleNode("ValidationStep");
				XmlNode contextConfig = testConfig.SelectSingleNode("ContextLoaderStep");

				context.LogInfo("Searching for files in: \"{0}{1}\"", directory, searchPattern );

				DateTime endTime = DateTime.Now + TimeSpan.FromMilliseconds(timeout);
				FileInfo[] files;

				do
				{
                    DirectoryInfo di = new DirectoryInfo(directory);
					files = di.GetFiles(searchPattern);

					Thread.Sleep(100);
				} while( (files.Length == 0) && (endTime > DateTime.Now) );

				if ( files.Length == 0 )
				{
					throw new ApplicationException(string.Format("No files were found at: {0}{1}", directory, searchPattern ) );
				}

				context.LogInfo("{0} fies were found at : \"{1}{2}\"", files.Length, directory, searchPattern );

				using( FileStream fs = new FileStream(files[0].FullName, FileMode.Open, FileAccess.Read) )
				{
					data = StreamHelper.LoadMemoryStream(fs);
				}

				context.LogData(string.Format("Loaded FILE: {0}", files[0].FullName), data);

				data.Seek(0, SeekOrigin.Begin);
				context.ExecuteContextLoader( data, contextConfig );

				data.Seek(0, SeekOrigin.Begin);
				context.ExecuteValidator( data, validationConfig );

				if ( deleteFile )
				{
					File.Delete( files[0].FullName );
				}
			}
			finally
			{
				if ( null != data )
				{
					data.Close();
				}
			}
		}
	}
}
